🌎 rns.kin.earth git 🌍
Commit 33ce3ef48f6ca5c4e03533d6be4f02e801d04bfc
Parents : 09a19ae
Author : Mark Qvist <mark@adepta.io>
Date : 2020-03-06T12:55:05+01:00
Implemented path requests
Changes
6 files changed, 107 insertions(+), 23 deletions(-)
Diff
diff --git a/Examples/Echo.py b/Examples/Echo.py
index 80c8c816..90bad019 100644
--- a/Examples/Echo.py
+++ b/Examples/Echo.py
@@ -96,8 +96,7 @@ def client(destination_hexhash, configpath, timeout=None):
# We override the loglevel to provide feedback when
# an announce is received
- # TODO: Reset this
- RNS.loglevel = RNS.LOG_DEBUG
+ RNS.loglevel = RNS.LOG_INFO
# Tell the user that the client is ready!
RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)")
@@ -108,14 +107,19 @@ def client(destination_hexhash, configpath, timeout=None):
# command line.
while True:
raw_input()
- # To address the server, we need to know it's public
- # key, so we check if Reticulum knows this destination.
- # This is done by calling the "recall" method of the
- # Identity module. If the destination is known, it will
- # return an Identity instance that can be used in
- # outgoing destinations.
- server_identity = RNS.Identity.recall(destination_hash)
- if server_identity != None:
+
+ # Let's first check if RNS knows a path to the destination.
+ # If it does, we'll load the server identity and create a packet
+ if RNS.Transport.hasPath(destination_hash):
+
+ # To address the server, we need to know it's public
+ # key, so we check if Reticulum knows this destination.
+ # This is done by calling the "recall" method of the
+ # Identity module. If the destination is known, it will
+ # return an Identity instance that can be used in
+ # outgoing destinations.
+ server_identity = RNS.Identity.recall(destination_hash)
+
# We got the correct identity instance from the
# recall method, so let's create an outgoing
# destination. We use the naming convention:
@@ -152,7 +156,8 @@ def client(destination_hexhash, configpath, timeout=None):
else:
# If we do not know this destination, tell the
# user to wait for an announce to arrive.
- RNS.log("Destination is not yet known. Wait for an announce to arrive and try again.")
+ RNS.log("Destination is not yet known. Requesting path...")
+ RNS.Transport.requestPath(destination_hash)
# This function is called when our reply destination
# receives a proof packet.
diff --git a/Notes/Header format b/Notes/Header format
index 0cd44629..66b1c8ec 100644
--- a/Notes/Header format
+++ b/Notes/Header format
@@ -28,3 +28,14 @@ data 00
announce 01
link request 10
proof 11
+
+
++- Header example -+
+
+01010000 00000100
+ | | | | |
+ | | | | +-- Context = RESOURCE_HMU
+ | | | +------- DATA packet
+ | | +--------- SINGLE destination
+ | +----------- TRANSPORT propagation type
+ +------------- HEADER_2, two byte header, two address fields
\ No newline at end of file
diff --git a/RNS/Destination.py b/RNS/Destination.py
index 8ba32246..76cd886d 100755
--- a/RNS/Destination.py
+++ b/RNS/Destination.py
@@ -75,8 +75,8 @@ class Destination:
if identity != None and type == Destination.SINGLE:
aspects = aspects+(identity.hexhash,)
- if identity == None and direction == Destination.IN:
- identity = Identity()
+ if identity == None and direction == Destination.IN and self.type != Destination.PLAIN:
+ identity = RNS.Identity()
aspects = aspects+(identity.hexhash,)
self.identity = identity
diff --git a/RNS/Interfaces/AX25KISSInterface.py b/RNS/Interfaces/AX25KISSInterface.py
index 140ee0a6..25610155 100644
--- a/RNS/Interfaces/AX25KISSInterface.py
+++ b/RNS/Interfaces/AX25KISSInterface.py
@@ -57,7 +57,8 @@ class AX25KISSInterface(Interface):
self.timeout = 100
self.online = False
# TODO: Sane default and make this configurable
- self.txdelay = 0.1
+ # TODO: Changed to 1ms instead of 100ms, check it
+ self.txdelay = 0.001
self.packet_queue = []
self.flow_control = flow_control
diff --git a/RNS/Packet.py b/RNS/Packet.py
index 9e63f768..5f3f1a2c 100755
--- a/RNS/Packet.py
+++ b/RNS/Packet.py
@@ -29,8 +29,9 @@ class Packet:
CACHE_REQUEST = 0x08 # Packet is a cache request
REQUEST = 0x09 # Packet is a request
RESPONSE = 0x0A # Packet is a response to a request
- COMMAND = 0x0B # Packet is a command
- COMMAND_STATUS = 0x0C # Packet is a status of an executed command
+ PATH_RESPONSE = 0x0B # Packet is a response to a path request
+ COMMAND = 0x0C # Packet is a command
+ COMMAND_STATUS = 0x0D # Packet is a status of an executed command
KEEPALIVE = 0xFB # Packet is a keepalive packet
LINKCLOSE = 0xFC # Packet is a link close message
LINKPROOF = 0xFD # Packet is a link packet proof
@@ -330,6 +331,9 @@ class PacketReceipt:
return False
elif len(proof) == PacketReceipt.IMPL_LENGTH:
# This is an implicit proof
+ if self.destination.identity == None:
+ return False
+
signature = proof[:RNS.Identity.SIGLENGTH/8]
proof_valid = self.destination.identity.validate(signature, self.hash)
if proof_valid:
diff --git a/RNS/Transport.py b/RNS/Transport.py
index a821b938..97d692b4 100755
--- a/RNS/Transport.py
+++ b/RNS/Transport.py
@@ -20,11 +20,13 @@ class Transport:
REACHABILITY_DIRECT = 0x01
REACHABILITY_TRANSPORT = 0x02
+ APP_NAME = "rnstransport"
+
# TODO: Document the addition of random windows
# and max local rebroadcasts.
PATHFINDER_M = 18 # Max hops
PATHFINDER_C = 2.0 # Decay constant
- PATHFINDER_R = 2 # Retransmit retries
+ PATHFINDER_R = 1 # Retransmit retries
PATHFINDER_T = 10 # Retry grace period
PATHFINDER_RW = 10 # Random window for announce rebroadcast
PATHFINDER_E = 60*15 # Path expiration in seconds
@@ -33,6 +35,9 @@ class Transport:
# various situations
LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed
+ PATH_REQUEST_GRACE = 0.25 # Grace time before a path announcement is made, allows directly reachable peers to respond first
+ PATH_REQUEST_RW = 2 # Path request random window
+
interfaces = [] # All active interfaces
destinations = [] # All active destinations
pending_links = [] # Links that are being established
@@ -78,7 +83,10 @@ class Transport:
except Exception as e:
RNS.log("Could not load packet hashlist from disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
-
+ # Create transport-specific destinations
+ path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
+ path_request_destination.packet_callback(Transport.pathRequestHandler)
+
thread = threading.Thread(target=Transport.jobloop)
thread.setDaemon(True)
thread.start()
@@ -121,12 +129,16 @@ class Transport:
announce_entry[1] = time.time() + math.pow(Transport.PATHFINDER_C, announce_entry[4]) + Transport.PATHFINDER_T + Transport.PATHFINDER_RW
announce_entry[2] += 1
packet = announce_entry[5]
+ block_rebroadcasts = announce_entry[7]
+ announce_context = RNS.Packet.NONE
+ if block_rebroadcasts:
+ announce_context = RNS.Packet.PATH_RESPONSE
announce_data = packet.data
announce_identity = RNS.Identity.recall(packet.destination_hash)
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
announce_destination.hash = packet.destination_hash
announce_destination.hexhash = announce_destination.hash.encode("hex_codec")
- new_packet = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, header_type = RNS.Packet.HEADER_2, transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash)
+ new_packet = RNS.Packet(announce_destination, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, header_type = RNS.Packet.HEADER_2, transport_type = Transport.TRANSPORT, transport_id = Transport.identity.hash)
new_packet.hops = announce_entry[4]
RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG)
outgoing.append(new_packet)
@@ -185,7 +197,6 @@ class Transport:
# Destination is directly reachable, and we know on
# what interface, so transmit only on that one
- # TODO: Strip transport headers here
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(outbound_interface), RNS.LOG_EXTREME)
RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
outbound_interface.processOutgoing(packet.raw)
@@ -212,7 +223,7 @@ class Transport:
packet.sent = True
packet.sent_at = time.time()
- if (packet.packet_type == RNS.Packet.DATA):
+ if (packet.packet_type == RNS.Packet.DATA and packet.destination.type != RNS.Destination.PLAIN):
packet.receipt = RNS.PacketReceipt(packet)
Transport.receipts.append(packet.receipt)
@@ -373,10 +384,13 @@ class Transport:
retries = 0
expires = now + Transport.PATHFINDER_E
local_rebroadcasts = 0
+ block_rebroadcasts = False
random_blobs.append(random_blob)
retransmit_timeout = now + math.pow(Transport.PATHFINDER_C, packet.hops) + (RNS.rand() * Transport.PATHFINDER_RW)
- Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, packet.hops, packet, local_rebroadcasts]
- Transport.destination_table[packet.destination_hash] = [now, received_from, packet.hops, expires, random_blobs, packet.receiving_interface]
+ if packet.context != RNS.Packet.PATH_RESPONSE:
+ Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, packet.hops, packet, local_rebroadcasts, block_rebroadcasts]
+
+ Transport.destination_table[packet.destination_hash] = [now, received_from, packet.hops, expires, random_blobs, packet.receiving_interface, packet]
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" is now via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG)
elif packet.packet_type == RNS.Packet.LINKREQUEST:
@@ -533,6 +547,55 @@ class Transport:
else:
cache_request_packet = RNS.Packet(Transport.transport_destination(), packet_hash, context = RNS.Packet.CACHE_REQUEST)
+ @staticmethod
+ def hasPath(destination_hash):
+ if destination_hash in Transport.destination_table:
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def requestPath(destination_hash):
+ path_request_data = destination_hash + RNS.Identity.getRandomHash()
+ path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
+ packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1)
+ packet.send()
+
+ @staticmethod
+ def pathRequestHandler(data, packet):
+ if len(data) >= RNS.Identity.TRUNCATED_HASHLENGTH/8:
+ Transport.pathRequest(data[:RNS.Identity.TRUNCATED_HASHLENGTH/8])
+
+ @staticmethod
+ def pathRequest(destination_hash):
+ RNS.log("Path request for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
+
+ local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
+ if local_destination != None:
+ RNS.log("Destination is local to this system, announcing", RNS.LOG_DEBUG)
+ local_destination.announce()
+
+ elif destination_hash in Transport.destination_table:
+ RNS.log("Path found, inserting announce for transmission", RNS.LOG_DEBUG)
+ packet = Transport.destination_table[destination_hash][6]
+ received_from = Transport.destination_table[destination_hash][5]
+
+ # Setting hops to 0xFF ensures announce is not rebroadcast by any local
+ # nodes, but requester will still see it and get a valid path
+ # TODO: Consider if there is a more elegant way to do this, or whether
+ # rebroadcasts should actually be allowed here
+ faux_hops = packet.hops
+ now = time.time()
+ retries = Transport.PATHFINDER_R
+ local_rebroadcasts = 0
+ block_rebroadcasts = True
+ retransmit_timeout = now + Transport.PATH_REQUEST_GRACE # + (RNS.rand() * Transport.PATHFINDER_RW)
+
+ Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, faux_hops, packet, local_rebroadcasts, block_rebroadcasts]
+
+ else:
+ RNS.log("No known path to requested destination, ignoring request", RNS.LOG_DEBUG)
+
@staticmethod
def transport_destination():
# TODO: implement this
Served by rngit 1.5.2 - Generated in 0.06s